package util

import (
	"ydd-im/config"
	"ydd-im/service/api/define"
	"ydd-im/service/api/tools/crypto"
	"crypto/md5"
	"encoding/hex"
	"errors"
	"fmt"
	"reflect"
	"regexp"

	uuid "github.com/satori/go.uuid"
	"math/rand"
	"net"
	"strconv"
	"strings"
	"time"
)

//GenUUID 生成uuid
func GenUUID() string {
	uuidFunc := uuid.NewV4()
	uuidStr := uuidFunc.String()
	uuidStr = strings.Replace(uuidStr, "-", "", -1)
	uuidByt := []rune(uuidStr)
	return string(uuidByt[8:24])
}

//对称加密IP和端口，当做clientId
func GenClientId() string {
	raw := []byte(define.LocalHost + ":" + define.RPCPort)
	key :=config.GetApiConfig().ApiCommon.CryptoKey
	str, err := crypto.Encrypt(raw, []byte(key))
	if err != nil {
		panic(err)
	}

	return str
}

//解析redis的地址格式
func ParseRedisAddrValue(redisValue string) (host string, port string, err error) {
	if redisValue == "" {
		err = errors.New("解析地址错误")
		return
	}
	addr := strings.Split(redisValue, ":")
	if len(addr) != 2 {
		err = errors.New("解析地址错误")
		return
	}
	host, port = addr[0], addr[1]

	return
}

//判断地址是否为本机
func IsAddrLocal(host string, port string) bool {
	return host == define.LocalHost && port == define.RPCPort
}

//是否集群
func IsCluster() bool {
	cluster := config.GetApiConfig().ApiCommon.Cluster

	return cluster
}

//生成RPC通信端口号，目前是ws端口号+1000
func GenRpcPort(port string) string {
	iPort, _ := strconv.Atoi(port)
	return strconv.Itoa(iPort + 1000)
}

//获取client key地址信息
func GetAddrInfoAndIsLocal(clientId string) (addr string, host string, port string, isLocal bool, err error) {
	//解密ClientId
	key :=config.GetApiConfig().ApiCommon.CryptoKey
	addr, err = crypto.Decrypt(clientId, []byte(key))
	if err != nil {
		return
	}

	host, port, err = ParseRedisAddrValue(addr)
	if err != nil {
		return
	}

	isLocal = IsAddrLocal(host, port)
	return
}

//获取本机内网IP
func GetIntranetIp() string {
	addrs, _ := net.InterfaceAddrs()

	for _, addr := range addrs {
		// 检查ip地址判断是否回环地址
		if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
			if ipnet.IP.To4() != nil {
				return ipnet.IP.String()
			}

		}
	}

	return ""
}

func GenGroupKey(systemId, groupName string) string {
	return systemId + ":" + groupName
}


//小写的
func Md5Encode(data string) string{
	h := md5.New()
	h.Write([]byte(data)) // 需要加密的字符串为 123456
	cipherStr := h.Sum(nil)
	return  hex.EncodeToString(cipherStr)
}
//大写
func MD5Encode(data string) string{
	return strings.ToUpper(Md5Encode(data))
}

func ValidatePasswd(plainpwd,salt,passwd string) bool{
	return Md5Encode(plainpwd+salt)==passwd
}
func MakePasswd(plainpwd,salt string) string{
	return Md5Encode(plainpwd+salt)
}
// 生成随机字符串
func  GetRandomString(l int) string {
	str := "0123456789abcdefghijklmnopqrstuvwxyz"
	bytes := []byte(str)
	result := []byte{}
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	for i := 0; i < l; i++ {
		result = append(result, bytes[r.Intn(len(bytes))])
	}
	return string(result)
}

// 获取当前的时间 - 字符串
func GetCurrentDate() string {
	return time.Now().Format("2006/01/02 15:04:05")
}

// 获取当前的时间 - Unix时间戳
func GetCurrentUnix() int64 {
	return time.Now().Unix()
}

// 获取当前的时间 - 毫秒级时间戳
func GetCurrentMilliUnix() int64 {
	return time.Now().UnixNano() / 1000000
}

// 获取当前的时间 - 纳秒级时间戳
func GetCurrentNanoUnix() int64 {
	return time.Now().UnixNano()
}
// 获取当前的时间 - 字符串
func GetTimeDate(times time.Time) string {
	return times.Format("2006-01-02 15:04:05")
}

// 获取当前的时区的时间，time.now用此方法
func GetCurrentDateTime() time.Time {
	loc, _:= time.LoadLocation("Asia/Chongqing")  //参数就是解压文件的“目录”+“/”+“文件名”。
	return time.Now().In(loc)
}


//slice去重
func RemoveRepByMap(slc []int) []int {
	result := []int{}         //存放返回的不重复切片
	tempMap := map[int]byte{} // 存放不重复主键
	for _, e := range slc {
		l := len(tempMap)
		tempMap[e] = 0 //当e存在于tempMap中时，再次添加是添加不进去的，，因为key不允许重复
		//如果上一行添加成功，那么长度发生变化且此时元素一定不重复
		if len(tempMap) != l { // 加入map后，map长度变化，则元素不重复
			result = append(result, e) //当元素不重复时，将元素添加到切片result中
		}
	}
	return result
}

//slice字符串去重
func RemoveRepByMapString(slc []string) []string {
	result := []string{}         //存放返回的不重复切片
	tempMap := map[string]byte{} // 存放不重复主键
	for _, e := range slc {
		l := len(tempMap)
		tempMap[e] = 0 //当e存在于tempMap中时，再次添加是添加不进去的，，因为key不允许重复
		//如果上一行添加成功，那么长度发生变化且此时元素一定不重复
		if len(tempMap) != l { // 加入map后，map长度变化，则元素不重复
			result = append(result, e) //当元素不重复时，将元素添加到切片result中
		}
	}
	return result
}
// 64位整数转int
func Int64Toint(id64 int64) int  {
	strInt64 := strconv.FormatInt(id64, 10)
	id ,err := strconv.Atoi(strInt64)
	if err != nil {
		return  0
	}
	return id
}
// int转64位整数
func IntToint64(id int) int64  {
	// int到string
	string := strconv.Itoa(id)
	// string到int64
	id64, err := strconv.ParseInt(string, 10, 64)
	if err != nil {
		return  0
	}
	return id64
}
// 判断类型
func DataisType( items interface{}) {
	switch vv := items.(type) {
	case string:
		fmt.Println( "type: string\nvalue: ", vv)
		fmt.Println("------------------")
	case float64:
		fmt.Println( "type: float64\nvalue: ", vv)
		fmt.Println("------------------")
	case bool:
		fmt.Println( "type: bool\nvalue: ", vv)
		fmt.Println("------------------")
	case map[string]interface{}:
		fmt.Println( "type: map[string]interface{}\nvalue: ", vv)
		for i, j := range vv {
			fmt.Println(i,": ",j)
		}
		fmt.Println("------------------")
	case []interface{}:
		fmt.Println( "type: []interface{}\nvalue: ", vv)
		for key, value := range vv {
			fmt.Println(key, ": ", value)
		}
		fmt.Println("------------------")
	default:
		fmt.Println( "type: nil\nvalue: ", vv)
		fmt.Println("------------------")
	}
}

// 正则过滤sql注入的方法
// 参数 : 要匹配的语句
func FilteredSQLInject(to_match_str string) bool {
	//过滤 ‘
	//ORACLE 注解 --  /**/
	//关键字过滤 update ,delete
	// 正则的字符串, 不能用 " " 因为" "里面的内容会转义
	str := `(?:')|(?:--)|(/\\*(?:.|[\\n\\r])*?\\*/)|(\b(select|update|and|or|delete|insert|trancate|char|chr|into|substr|ascii|declare|exec|count|master|into|drop|execute)\b)`
	re, err := regexp.Compile(str)
	if err != nil {
		return false
	}

	return re.MatchString(to_match_str)
}
//struct转map
func StructToMapDemo(obj interface{}) map[string]interface{}{
	obj1 := reflect.TypeOf(obj)
	obj2 := reflect.ValueOf(obj)
	var data = make(map[string]interface{})
	for i := 0; i < obj1.NumField(); i++ {
		data[obj1.Field(i).Name] = obj2.Field(i).Interface()
	}
	return data
}